home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / which213.zip / src / which.c < prev    next >
C/C++ Source or Header  |  1997-06-03  |  29KB  |  957 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.     which.c
  4.  
  5.     This program by default finds the path of an executable command under
  6.     either OS/2 or MS-DOS.  It can also be used to find data files in the
  7.     DPATH or DLLs in the LIBPATH (at least under OS/2).  It does not have
  8.     a whole lot of error-checking, but the array sizes are reasonably gen-
  9.     erous.
  10.  
  11.     To do:  add support for ksh, bash, etc.; allow wildcard commands?
  12.  
  13.     Copyright (c) 1993 by Greg Roelofs.  4OS2/4DOS spawnl() code by Michael
  14.     D. Lawler.    DosQuerySysInfo() code by Kenneth Porter.  You may use this
  15.     code for any purpose whatsoever, as long as you don't sell it and don't
  16.     claim to have written it.  Not that you'd want to.
  17.  
  18.     Modified by Wonkoo Kim (wkim+@pitt.edu):
  19.     (The original starting version was v2.1)
  20.  
  21.     v2.1.1  September 15, 1995
  22.     - Wildcard support (for emx/gcc) is added.
  23.     - Added option: -s Show file size and date.
  24.  
  25.     v2.1.2  January 14, 1996
  26.     - Added options:
  27.         -h     Search *.hlp in HELP dir.
  28.         -b     Search *.inf in BOOKSHELF dir.
  29.         -e * Search dirs set by env vars.
  30.  
  31.     v2.1.3  June 3, 1997
  32.     - Option -l now searches BEGINLIBPATH + LIBPATH + ENDLIBPATH.
  33.         (Thanks to "Bruce A. Mallett" <bam@NightStorm.com>)
  34.     - New options:
  35.         -1     Show only the first match from all command line args, i.e.
  36.          displays a single path name if found (useful in makefile)
  37.          (-1 + -a for all first matches of command line args)
  38.         -u     Unix style path names (use forward slashes as dir separators)
  39.     - Minor refinements
  40.  
  41.  
  42.   ---------------------------------------------------------------------------*/
  43.  
  44. #define VERSION   "v2.1.3 June-03-1997 updated by Wonkoo Kim"
  45.  
  46. /* #define DEBUG */
  47.  
  48. #include <stdio.h>
  49. #include <stdlib.h>
  50. #include <ctype.h>
  51. #include <process.h>
  52. #include <string.h>
  53. #include <sys/types.h>
  54. #include <sys/stat.h>
  55. #include <time.h>
  56.  
  57. #ifndef TRUE
  58. #  define TRUE     1
  59. #  define FALSE  0
  60. #endif
  61.  
  62. /* not used:
  63.     #if defined(__OS2__) && !defined(OS2)
  64.     #  define OS2
  65.     #endif
  66.  */
  67.  
  68. #if defined(__IBMC__)
  69. #  define S_IFMT   0xF000   /* or (S_IFREG | S_IFDIR | S_IFCHR) */
  70. #endif
  71. #ifndef S_ISDIR
  72. #  define S_ISDIR(m)   (((m) & S_IFMT) == S_IFDIR)
  73. #endif
  74.  
  75. #if defined(__IBMC__) || defined(__EMX__) || defined(__WATCOMC__)
  76. #  ifndef __32BIT__
  77. #    define __32BIT__
  78. #  endif
  79. #  define INCL_DOSMISC         /* for IBM Toolkit headers */
  80. #  define INCL_DOSERRORS     /* for IBM Toolkit headers */
  81. #  define INCL_NOPMAPI         /* for IBM Toolkit headers */
  82. #  include <os2.h>
  83. #if defined(__EMX__)
  84. #  include <fnmatch.h>        /* for wildcard support -- Wonkoo Kim */
  85. #endif
  86. #endif
  87.  
  88. #ifdef DEBUG
  89. #  define Trace(x)   fprintf x
  90. #else
  91. #  define Trace(x)
  92. #endif
  93.  
  94.  
  95. int get_libpath(char **path);
  96. int get_helppath(char **path);
  97. int get_bookpath(char **path);
  98. int printmsg (char *msg, int pos);
  99. void pretty_path (char *path, char dir_sep);
  100.  
  101.  
  102. struct stat statbuf;
  103.  
  104.  
  105. /* extensions must be in order of operating-system precedence! */
  106.  
  107. static char *ext_command[] = {       /* COMMAND extensions */
  108.     ".com",
  109.     ".exe",
  110.     ".bat"
  111. };
  112.  
  113. static char *ext_cmd[] = {       /* CMD extensions */
  114.     ".com",
  115.     ".exe",
  116.     ".cmd",
  117.     ".bat"
  118. };
  119.  
  120. static char *ext_4dos[] = {       /* 4DOS extensions */
  121.     ".com",
  122.     ".exe",
  123.     ".btm",
  124.     ".bat"
  125. };
  126.  
  127. static char *ext_4os2[] = {       /* 4OS2 extensions */
  128.     ".com",
  129.     ".exe",
  130.     ".btm",
  131.     ".cmd",
  132.     ".bat"
  133. };
  134.  
  135. static char *ext_libpath[] = {       /* LIBPATH extension(s) */
  136.     ".dll"
  137. };
  138.  
  139. static char *ext_dpath[] = {       /* DPATH extension(s) */
  140.     ".boo",
  141.     ".dat",
  142.     ".inf",
  143.     ".ini",   /* does this belong here? */
  144.     ".hlp",
  145.     ".msg",
  146.     ".ndx"
  147. };
  148.  
  149. static char *ext_helppath[] = {     /* HELP extension(s) */
  150.     ".hlp"
  151. };
  152.  
  153. static char *ext_bookpath[] = {     /* BOOKSHELF extension(s) */
  154.     ".inf"
  155. };
  156.  
  157. static char *ext_envpath[] = {       /* ENV extension(s) */
  158.     ".*"
  159. };
  160.  
  161.  
  162. /* OS/2 internal commands:  some of these are rarely used outside of
  163.  * command files, but all of them can be called from the command line.
  164.  * (I don't know if some of the OS/2-only commands might be in later
  165.  * versions of MS-DOS.    One could use spawnl() and check the errors,
  166.  * but that would be slow.)
  167.  */
  168.  
  169. static char *int_command[] = {
  170.     "call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
  171.     "erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
  172.     "prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
  173.     "time", "type", "ver", "verify", "vol"
  174. };
  175.  
  176. static char *int_cmd[] = {
  177.  
  178.     /* note that extproc cannot be called from command line in 4OS2 */
  179.     "chcp", "detach", "dpath", "endlocal", "extproc", "keys", "move",
  180.     "setlocal", "start",
  181.  
  182.     /* all the rest are identical to int_command[] */
  183.     "call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
  184.     "erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
  185.     "prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
  186.     "time", "type", "ver", "verify", "vol"
  187. };
  188.  
  189.  
  190. typedef struct shellinfo {
  191.     char *name;
  192.     char **extension;
  193.     int num_ext;
  194.     char **internal;
  195.     int num_int;
  196. } SHELLINFO;
  197.  
  198. /* these guys must match the order of shells[] */
  199. #define _COMMAND   0
  200. #define _CMD       1
  201. #define _4DOS       2
  202. #define _4OS2       3
  203. #define _4OS2_16   4
  204. #define _4OS2_32   5
  205.  
  206. SHELLINFO shells[] = {
  207.     {"COMMAND.COM", ext_command,   sizeof(ext_command)/sizeof(char *),
  208.             int_command,   sizeof(int_command)/sizeof(char *) },
  209.     {"CMD.EXE",     ext_cmd,       sizeof(ext_cmd)/sizeof(char *),
  210.             int_cmd,       sizeof(int_cmd)/sizeof(char *) },
  211.     {"4DOS.COM",    ext_4dos,      sizeof(ext_4dos)/sizeof(char *),
  212.             (char **)NULL, 0 },
  213.     {"4OS2.EXE",    ext_4os2,      sizeof(ext_4os2)/sizeof(char *),
  214.             (char **)NULL, 0 },
  215.     {"4OS2-16.EXE", ext_4os2,      sizeof(ext_4os2)/sizeof(char *),
  216.             (char **)NULL, 0 },
  217.     {"4OS2-32.EXE", ext_4os2,      sizeof(ext_4os2)/sizeof(char *),
  218.             (char **)NULL, 0 }
  219. };
  220.  
  221. #define REGPATH    0   /* for pathtype */
  222. #define LIBPATH    1
  223. #define DPATH       2
  224. #define HELPPATH   3
  225. #define BOOKPATH   4
  226. #define ENVPATH    5
  227.  
  228. char *prognam;
  229.  
  230.  
  231. /****************/
  232. /* Main program */
  233. /****************/
  234.  
  235. int main(int argc, char *argv[])
  236. {
  237.     static char tempname[1024];
  238.     char *p, *q, *comspec, *path, *pathvar="", *dir[1000];
  239.     int i, j, k, c, error=0, startdir=0;
  240.     int sh=_CMD, all=FALSE, pathtype=REGPATH, regpath=TRUE, fourshell=FALSE;
  241.     int firstmatch=FALSE;
  242.     int numdirs=0, numshells=sizeof(shells)/sizeof(SHELLINFO);
  243.     int showinfo=FALSE, quiet=FALSE;
  244.     int found = 0;
  245.     char info[20];
  246.     char *envpath = "", envvar[100];
  247.     char dir_separator = '\\';          /* OS/2 or DOS style dir separator */
  248.  
  249. /*---------------------------------------------------------------------------
  250.     Parse the command line...
  251.   ---------------------------------------------------------------------------*/
  252.  
  253. #ifdef __EMX__
  254.     prognam = _getname (argv[0]);   /* remove pathname */
  255. #else
  256.     prognam = argv[0];
  257. #endif
  258.     p = prognam - 1;
  259.     while (*++p)
  260.     *p = tolower(*p);   /* assumes "smart" tolower() */
  261.  
  262.     if (argc <= 1)
  263.     --argc;
  264.     else
  265.     while (--argc > 0  &&  (*++argv)[0] == '-')
  266.         while ((c = *++(argv[0])) != '\0')
  267.         switch (c) {
  268.             case 'a':             /* list all matches */
  269.             all = TRUE;
  270.             break;
  271.             case 'l':             /* search for DLLs */
  272.             pathtype = LIBPATH;
  273.             break;
  274.             case 'd':             /* search for data files */
  275.             pathtype = DPATH;
  276.             break;
  277.             case 'h':             /* search for help files */
  278.             pathtype = HELPPATH;
  279.             break;
  280.             case 'b':             /* search for book files */
  281.             pathtype = BOOKPATH;
  282.             break;
  283.             case 'e':             /* search files in paths set by env */
  284.             pathtype = ENVPATH;
  285.             if (*++(argv[0])) {
  286.                 strcpy (envvar, argv[0]);
  287.                 envpath = getenv(envvar);
  288.                 *(argv[0]+1) = '\0';
  289.             } else if (--argc > 0 && (*++argv)[0]) {
  290.                 strcpy (envvar, argv[0]);
  291.                 envpath = getenv(envvar);
  292.                 *(argv[0]+1) = '\0';
  293.             } else
  294.                 error++;
  295.             break;
  296.             case '1':           /* find the first match of all cmd args */
  297.             firstmatch = TRUE;
  298.             break;
  299.             case 's':             /* show file information */
  300.             showinfo = TRUE;
  301.             break;
  302.             case 'q':             /* quite mode (return errlevel) */
  303.             quiet = TRUE;
  304.             break;
  305.             case 'u':           /* unix style path (forward slashes) */
  306.             dir_separator = '/';
  307.             break;
  308.             default:
  309.             ++error;
  310.             break;
  311.         } /* end switch, while, while, if */
  312.  
  313. /*---------------------------------------------------------------------------
  314.     Print usage if any errors or if no arguments.
  315.   ---------------------------------------------------------------------------*/
  316.  
  317.     if (error || ((pathtype == REGPATH) && (argc <= 0))) {
  318. #ifdef TRADITIONAL
  319.     fprintf(stderr, "%s: too few arguments\n", prognam);
  320. #else
  321.     fprintf(stderr, "\n"
  322. "which (%s) for OS/2%s, from Newtware\n\n"
  323. "Usage:  %s [options] [cmd ... ]\n\n"
  324. "Options:\n"
  325. "    (default behavior is to find location of program executed as \"cmd\")\n"
  326. "    -1  show the first match from all cmd args; display nothing if not found\n"
  327. "    -a  list all matches (With -1, list all first matches of cmd args)\n"
  328. "    -b  search directories in BOOKSHELF for book (.inf) files%s\n"
  329. "    -d  search directories in DPATH for data files%s\n"
  330. "    -e  search directories in a given env var; Exapmple: -e EPMPATH\n"
  331. "    -h  search directories in HELP for help (.hlp) files%s\n"
  332. "    -l  search directories in LIBPATH (BEGIN/ENDLIBPATH) for DLLs%s\n"
  333. "    -q  quiet mode (only returns error code without onscreen messages)\n"
  334. "    -s  show file date/time and size\n"
  335. "    -u  unix style path names (use forward-slashes as dir separators)\n\n"
  336. "   cmd  name to be searched (wildcards can be used)\n\n"
  337. "Examples:  %s -las emx*\n"
  338. "           %s -las foo*.bar  (override the default extension)\n"
  339. , VERSION,
  340. #if defined(__32BIT__) && !defined(EMX)
  341.         "", prognam, "", "", "", "",
  342. #else
  343.         " & DOS", prognam, " (OS/2)", " (OS/2)",  " (OS/2)", " (OS/2)",
  344. #endif /* ?__32BIT__ */
  345.         prognam, prognam);
  346. #endif /* ?TRADITIONAL */
  347.     exit(1);
  348.     }
  349.  
  350. /*---------------------------------------------------------------------------
  351.     Try to figure out what shell we're in, based on COMSPEC.
  352.   ---------------------------------------------------------------------------*/
  353.  
  354.     if ((comspec = getenv("COMSPEC")) == (char *)NULL || *comspec == '\0') {
  355.     Trace((stderr, "COMSPEC is empty...assuming COMSPEC = cmd.exe\n"));
  356.     sh = _CMD;
  357.     } else {
  358.     Trace((stderr, "COMSPEC = %s\n", comspec));
  359.     if ((p = strrchr (comspec, '\\')))
  360.         p++;
  361.     else if ((p = strrchr (comspec, '/')))
  362.         p++;
  363.     else
  364.         p = comspec;
  365.     for (i = 0;  i < numshells;  ++i) {
  366.         if (stricmp(p, shells[i].name) == 0) {
  367.         sh = i;
  368.         break;
  369.         }
  370.     }
  371.     if (i == numshells) {
  372.         sh = _CMD;
  373.         fprintf(stderr,
  374.           "%s: unknown command shell \"%s\"\n%s: assuming %s\n",
  375.           prognam, comspec, prognam, shells[sh].name);
  376.     }
  377.     if (sh >= 2 && sh <= 5)   /* 4DOS, 4OS2, 4OS2-16, 4OS2-32 */
  378.         fourshell = TRUE;
  379.     }
  380.     Trace((stderr, "shell is %s\n\n", shells[sh].name));
  381.  
  382. /*---------------------------------------------------------------------------
  383.     Get the PATH, DPATH or LIBPATH, depending on the user's wishes.
  384.   ---------------------------------------------------------------------------*/
  385.  
  386.     /* for regular path, current directory is always implied; not for others */
  387.     switch (pathtype) {
  388.     case REGPATH:
  389.         pathvar = "PATH";
  390.         regpath = TRUE;
  391.         dir[numdirs++] = ".";
  392.         path = getenv(pathvar);
  393.         break;
  394.     case DPATH:
  395.         pathvar = "DPATH";
  396.         regpath = FALSE;
  397.         shells[sh].extension = ext_dpath;
  398.         shells[sh].num_ext = sizeof(ext_dpath)/sizeof(char *);
  399.         path = getenv(pathvar);
  400.         break;
  401.     case LIBPATH:
  402.         pathvar = "LIBPATH";
  403.         regpath = FALSE;
  404.         shells[sh].extension = ext_libpath;
  405.         shells[sh].num_ext = sizeof(ext_libpath)/sizeof(char *);
  406.         get_libpath(&path);
  407.         break;
  408.     case HELPPATH:
  409.         pathvar = "HELP";
  410.         regpath = FALSE;
  411.         shells[sh].extension = ext_helppath;
  412.         shells[sh].num_ext = sizeof(ext_helppath)/sizeof(char *);
  413.         path = getenv(pathvar);
  414.         break;
  415.     case BOOKPATH:
  416.         pathvar = "BOOKSHELF";
  417.         regpath = FALSE;
  418.         shells[sh].extension = ext_bookpath;
  419.         shells[sh].num_ext = sizeof(ext_bookpath)/sizeof(char *);
  420.         path = getenv(pathvar);
  421.         break;
  422.     case ENVPATH:
  423.         pathvar = envvar;
  424.         regpath = FALSE;
  425.         shells[sh].extension = ext_envpath;
  426.         shells[sh].num_ext = sizeof(ext_envpath)/sizeof(char *);
  427.         path = envpath;
  428.         break;
  429.     }
  430.     Trace((stderr, "COMSPEC now = %s\n", comspec));
  431.     if (argc <= 0) {
  432.     printf ("%s=%s\n", pathvar, path);
  433.     exit (0);
  434.     }
  435.  
  436. /*---------------------------------------------------------------------------
  437.     Terminate the path elements and store pointers to each one.
  438.   ---------------------------------------------------------------------------*/
  439.  
  440.     if (path == (char *)NULL || *path == '\0') {
  441.     Trace((stderr, "\n%s is empty\n\n", pathvar));
  442.     if (!regpath) {
  443.         fprintf(stderr, "%s: %s is empty\n", prognam, pathvar);
  444.         exit (2);
  445.     }
  446.     } else {
  447.     pretty_path (path, dir_separator);
  448.     Trace((stderr, "\n%s = %s\n\n", pathvar, path));
  449.     if (*path != ';')
  450.         dir[numdirs++] = path;
  451.     p = path - 1;
  452.     while (*++p)
  453.         if (*p == ';') {
  454.         *p = '\0';
  455.         if ((p[1] != '\0') && (p[1] != ';'))
  456.             dir[numdirs++] = p + 1;
  457.         } else
  458.         *p = tolower(*p);  /* should probably make this an option... */
  459.     }
  460.  
  461. /*---------------------------------------------------------------------------
  462.     If we're doing a normal PATH search under 4OS2 or 4DOS, check the path
  463.     for a "." entry; if find one, ignore the extra "." entry which was previ-
  464.     ously inserted at the beginning of the dir[] array.  (Entries of the form
  465.     "d:." don't count, and CMD and COMMAND always insert the "." first.)
  466.   ---------------------------------------------------------------------------*/
  467.  
  468.     if (fourshell && pathtype == REGPATH) {
  469.     for (i = 1;  i < numdirs;  ++i)
  470.         if (dir[i][0] == '.' && dir[i][1] == '\0') {    /* "." */
  471.         startdir = 1;
  472.         break;
  473.         }
  474.     }
  475.  
  476. /*---------------------------------------------------------------------------
  477.     For each command or file given as an argument, check all of the direc-
  478.     tories in the appropriate path.  For commands, first see if it's an in-
  479.     ternal command or (in the case of 4DOS/4OS2) an alias; if not, search
  480.     the path for it.  For each directory in the path, see if the OS will con-
  481.     sider it a command as is (it has a dot in its name), and if so whether
  482.     it exists; then try appending each extension (in order of precedence)
  483.     and again check for existence.  For data files or DLLs, just check direc-
  484.     tories in the path for the filename or the filename with an appropriate
  485.     extension (".dll", ".inf", etc.) appended.
  486.   ---------------------------------------------------------------------------*/
  487.  
  488.     for (j = 0;  j < argc;  ++j) {
  489.     int hasdot;
  490.     int pos=0;
  491. #ifdef __EMX__
  492.     int wildcard;
  493.     char **list;
  494.     int l;
  495.     wildcard = (strchr(argv[j], (int)'?') != (char *)NULL) ||
  496.         (strchr(argv[j], (int)'*') != (char *)NULL);
  497. #endif
  498.     /* don't bother with internal commands if argument has a dot */
  499.     hasdot = (strchr(argv[j], (int)'.') != (char *)NULL);
  500.  
  501.     if (!quiet && j) {
  502.         if (! firstmatch)
  503.         printf ("\n");
  504.         else if (found && ! showinfo)
  505.         printf (" ");
  506.     }
  507.     found = 0;
  508.  
  509.     if (regpath && !hasdot) {
  510.         Trace((stderr, " checking %s internals\n", shells[sh].name));
  511.  
  512.         /* 4DOS/4OS2 have tests for internals/aliases, so check smartly */
  513.         if (fourshell) {
  514.         char *tempenv[2] = {tempname, NULL};
  515.         int rc;
  516.  
  517. #ifdef COMMANDSEP_WORKS   /* This form always fails (get correct response only
  518.                * if first condition is true, i.e., both alias and
  519.                * internal command).  It appears to be another bug
  520.                * in 4OS2 (rc always = 0).  The line is too long for
  521.                * 4DOS in any case (257 bytes for 5-char alias name).
  522.                */
  523.         sprintf(tempname, "4WHICH=iff isalias %s .and. isinternal %s "
  524.           "then %%+ echos %s:  aliased to \"%%@alias[%s]\" %%+ exit 43 %%+"
  525.           " elseiff isalias %s then %%+ echos %s:  aliased to \"%%@alias"
  526.           "[%s]\" %%+ exit 42 %%+ elseiff isinternal %s then %%+ exit 41 "
  527.           "%%+ else %%+ exit 40 %%+ endiff", argv[j], argv[j],
  528.           argv[j], argv[j], argv[j], argv[j], argv[j], argv[j]);
  529.         Trace((stderr, "4WHICH length = %d bytes\n\n%s\n\n",
  530.           strlen(tempname), tempname));
  531.         rc = spawnle(P_WAIT, comspec, comspec, "/c", "%4which%", NULL,
  532.           tempenv);
  533.         Trace((stderr, "4WHICH return code = %d\n", rc));
  534. #else /* !COMMANDSEP_WORKS */
  535.         /* quotes around alias necessary due to 4DOS/4OS2 bug */
  536.         if (sh != _4DOS) {   /* 4OS2:  can do in one long command */
  537.             sprintf(tempname, "4WHICH=iff isalias %s .and. isinternal "
  538.               "%s then & echos %s:  aliased to \"%%@alias[%s]\" & exit "
  539.               "43 & elseiff isalias %s then & echos %s:  aliased to "
  540.               "\"%%@alias[%s]\" & exit 42 & elseiff isinternal %s then "
  541.               "& exit 41 & else & exit 40 & endiff", argv[j], argv[j],
  542.               argv[j], argv[j], argv[j], argv[j], argv[j], argv[j]);
  543.             Trace((stderr, "4WHICH length = %d bytes\n\n%s\n\n",
  544.               strlen(tempname), tempname));
  545.             /* "/c", "4which" arguments must be separate for emx+gcc */
  546.             rc = spawnle(P_WAIT, comspec, comspec, "/c", "%4which%",
  547.               NULL, tempenv);
  548.             Trace((stderr, "4WHICH return code = %d\n", rc));
  549.             if (rc == 42 || rc == 43)  /* alias (and internal if 43) */
  550.             ++found;
  551.         } else {
  552.             sprintf(tempname, "4WHICH=iff isalias %s .and. isinternal "
  553.               "%s then ^ exit 43 ^ elseiff isalias %s then ^ exit 42 ^ "
  554.               "elseiff isinternal %s then ^ exit 41 ^ else ^ exit 40 ^ "
  555.               "endiff", argv[j], argv[j], argv[j], argv[j]);
  556.             Trace((stderr, "4WHICH length = %d bytes\n\n%s\n\n",
  557.               strlen(tempname), tempname));
  558.             rc = spawnle(P_WAIT, comspec, comspec, "/c", "%4which%", NULL,
  559.               tempenv);
  560.             Trace((stderr, "4WHICH return code = %d\n", rc));
  561.             if (rc == 42 || rc == 43) {   /* alias */
  562.             ++found;
  563.             sprintf(tempname, "echos %s:  aliased to \"%%@alias"
  564.               "[%s]\"", argv[j], argv[j]);
  565.             Trace((stderr, "2nd cmd length = %d bytes\n\n%s\n\n",
  566.               strlen(tempname), tempname));
  567.             spawnl(P_WAIT, comspec, comspec, "/c", tempname, NULL);
  568.             }
  569.         }
  570. #endif /* ?COMMANDSEP_WORKS */
  571.         if (rc == 41 || (rc == 43 && all && !firstmatch)) {   /* internal */
  572.             ++found;
  573.             sprintf(tempname, "%s internal command", shells[sh].name);
  574.             if (rc == 41)
  575.             printf("%s:  %s", argv[j], tempname);
  576.             else
  577.             printf(" (also %s", tempname);
  578.         }
  579.         } else {
  580.         /* quit as soon as found:  only one internal match allowed */
  581.         for (i = 0;  i < shells[sh].num_int;  ++i) {
  582.             Trace((stderr, " checking %s\n", shells[sh].internal[i]));
  583. #ifdef __EMX__
  584.             if (wildcard) {
  585.             if (fnmatch(argv[j], shells[sh].internal[i],
  586.               _FNM_IGNORECASE|_FNM_OS2) == 0) {
  587.                 found++;
  588.                 if (quiet) break;
  589.                 if (showinfo) {
  590.                 printf("%4d: %s:%*s %s internal command\n",
  591.                   found, shells[sh].internal[i],
  592.                   8-(int)strlen(shells[sh].internal[i]), "",
  593.                   shells[sh].name);
  594.                 } else {
  595.                 sprintf(tempname, "%s: %s internal command",
  596.                   shells[sh].internal[i], shells[sh].name);
  597.                 if (found == 2) {
  598.                     pos = printmsg (" (also ", -pos);
  599.                     pos = printmsg (tempname, -pos);
  600.                 } else {
  601.                     pos = printmsg (tempname, pos);
  602.                 }
  603.                 }
  604.                 if (!all || firstmatch)
  605.                 break;     /* quit right now unless finding all */
  606.             }
  607.             } else {
  608. #endif
  609.             if (stricmp(argv[j], shells[sh].internal[i]) == 0) {
  610.                 ++found;
  611.                 if (quiet) break;
  612.                 if (showinfo) {
  613.                 printf("%4d: %s:%*s %s internal command\n",
  614.                   found, argv[j], 8-(int)strlen(argv[j]), "",
  615.                   shells[sh].name);
  616.                 } else {
  617.                 printf("%s:%*s %s internal command\n",
  618.                   argv[j], 8-(int)strlen(argv[j]), "",
  619.                   shells[sh].name);
  620.                 }
  621.                 break;
  622.             }
  623. #ifdef __EMX__
  624.             }
  625. #endif
  626.         }
  627.         }
  628.     }
  629.     for (i = startdir;  (i < numdirs) && (!found || (all && !firstmatch)); ++i) {
  630.         p = tempname;
  631.         q = dir[i];
  632.         while ((*p++ = *q++) != '\0');      /* p now points to char *after* 0 */
  633.         if (p[-2] == '\\' || p[-2] == '/')  /* could be root dir (e.g., c:\) */
  634.         --p;
  635.         p[-1] = dir_separator;        /* replace null with dir sep. */
  636.         q = argv[j];
  637.         while ((*p++ = *q++) != '\0');  /* copy program name */
  638.         --p;                /* point at null */
  639.         if (hasdot) {
  640.         Trace((stderr, " checking %s\n", tempname));
  641. #ifdef __EMX__
  642.         list = _fnexplode (tempname);
  643.         if (list) {
  644.             for (l = 0; list[l] != NULL; ++l) {
  645.             if (!stat(list[l], &statbuf) && !S_ISDIR(statbuf.st_mode)) {
  646.                 found++;
  647.                 if (quiet) break;
  648.                 if (showinfo) {
  649.                 strftime (info, 20, "%T %D",
  650.                     localtime(&statbuf.st_mtime));
  651.                 printf ("%4d: %s %9ld  %s\n", found, info,
  652.                     statbuf.st_size, list[l]);
  653.                 } else {
  654.                 if (found == 2) {
  655.                     pos = printmsg (" (also ", -pos);
  656.                     pos = printmsg (list[l], -pos);
  657.                 } else
  658.                     pos = printmsg (list[l], pos);
  659.                 }
  660.                 if (!all || firstmatch)
  661.                 break;     /* quit right now unless finding all */
  662.             }
  663.             }
  664.             _fnexplodefree (list);
  665.         }
  666. #else
  667.         if (!stat(tempname, &statbuf) && !S_ISDIR(statbuf.st_mode)) {
  668.             ++found;
  669.             if (quiet) break;
  670.             if (showinfo) {
  671.             strftime (info, 20, "%T %D",
  672.                 localtime(&statbuf.st_mtime));
  673.             printf ("%4d: %s %9ld  %s\n", found, info,
  674.                 statbuf.st_size, tempname);
  675.             } else {
  676.             if (found == 2) {
  677.                 pos = printmsg (" (also ", -pos);
  678.                 pos = printmsg (tempname, -pos);
  679.             } else
  680.                 pos = printmsg (tempname, pos);
  681.             }
  682.             if (!all || firstmatch)
  683.             break;     /* quit right now unless finding all */
  684.         }
  685. #endif
  686.         }
  687.         for (k = 0;  (k < shells[sh].num_ext) && (!found || (all && !firstmatch)); ++k) {
  688.         strcpy(p, shells[sh].extension[k]);
  689.         Trace((stderr, " checking %s\n", tempname));
  690. #ifdef __EMX__
  691.         list = _fnexplode (tempname);
  692.         if (list) {
  693.             for (l = 0; list[l] != NULL; ++l) {
  694.             if (!stat(list[l], &statbuf) && !S_ISDIR(statbuf.st_mode)) {
  695.                 found++;
  696.                 if (quiet) break;
  697.                 if (showinfo) {
  698.                 strftime (info, 20, "%T %D",
  699.                     localtime(&statbuf.st_mtime));
  700.                 printf ("%4d: %s %9ld  %s\n", found, info,
  701.                     statbuf.st_size, list[l]);
  702.                 } else {
  703.                 if (found == 2) {
  704.                     pos = printmsg (" (also ", -pos);
  705.                     pos = printmsg (list[l], -pos);
  706.                 } else
  707.                     pos = printmsg (list[l], pos);
  708.                 }
  709.                 if (!all || firstmatch)
  710.                 break;     /* quit right now unless finding all */
  711.             }
  712.             }
  713.             _fnexplodefree (list);
  714.         }
  715. #else
  716.         if (!stat(tempname, &statbuf) && !S_ISDIR(statbuf.st_mode)) {
  717.             ++found;
  718.             if (quiet) break;
  719.             if (showinfo) {
  720.             strftime (info, 20, "%T %D",
  721.                 localtime(&statbuf.st_mtime));
  722.             printf ("%4d: %s %9ld  %s\n", found, info,
  723.                 statbuf.st_size, tempname);
  724.             } else {
  725.             if (found == 2) {
  726.                 pos = printmsg (" (also ", -pos);
  727.                 pos = printmsg (tempname, -pos);
  728.             } else
  729.                 pos = printmsg (tempname, pos);
  730.             }
  731.             if (!all || firstmatch)
  732.             break;     /* quit right now unless finding all */
  733.         }
  734. #endif
  735.         }
  736.     } /* end i-loop */
  737.     if (quiet) {
  738.         if (!found) exit (1);
  739.     } else {
  740.         if (!found && !firstmatch) {
  741.         printf("%s is NOT FOUND in:\n ", argv[j]);
  742.         pos = -1;
  743.         for (i = 0;  i < numdirs;  ++i) {
  744.             pos = printmsg (dir[i], pos);
  745.         }
  746.         }
  747.         if (!showinfo && found > 1) {
  748.         if (pos < 75)
  749.             printf (") %d\n", found);
  750.         else {
  751.             printf (")\n %d\n", found);
  752.         }
  753.         }
  754.         if (firstmatch && found && !all) break;
  755.     }
  756.     }
  757.     exit (0);
  758. }
  759.  
  760.  
  761.  
  762. /**************************/
  763. /* Function get_libpath() */
  764. /**************************/
  765.  
  766. int get_libpath(char **path)
  767. {
  768.     char *line, *s, *p, config_sys[]="c:\\config.sys";
  769.     char *libpath[3];
  770.     int i, len;
  771.     int foundcfg=FALSE;
  772. #ifdef __32BIT__
  773.     int drive;
  774.     APIRET rc;
  775. #endif
  776.     FILE *cfg=NULL;
  777.  
  778.     *path = (char *)NULL;
  779.  
  780. #ifdef __EMX__
  781.     if (_osmode == DOS_MODE) {
  782.     return -1;
  783.     }
  784. #endif
  785.  
  786.     if ((line = (char *)malloc(4096)) == NULL) {
  787.     fprintf(stderr, "%s: not enough memory\n", prognam);
  788.     exit(8);
  789.     }
  790.  
  791. #ifdef __32BIT__
  792.     rc = DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &drive, sizeof(drive));
  793.     if (rc == NO_ERROR) {
  794.     config_sys[0] = drive - 1 + 'a';
  795.     if (!stat(config_sys, &statbuf) && !S_ISDIR(statbuf.st_mode) &&
  796.         (cfg = fopen(config_sys, "r")) != NULL)
  797.         foundcfg = TRUE;
  798.     } else {
  799.     fprintf(stderr, "%s: internal error (%ld)\n", prognam, rc);
  800. #endif
  801.     if (!stat(config_sys+2, &statbuf) && !S_ISDIR(statbuf.st_mode) &&
  802.         (cfg = fopen(config_sys+2, "r")) != NULL)
  803.         foundcfg = TRUE;
  804.     else if (!stat(config_sys, &statbuf) && !S_ISDIR(statbuf.st_mode) &&
  805.         (cfg = fopen(config_sys, "r")) != NULL)
  806.         foundcfg = TRUE;
  807. #ifdef __32BIT__
  808.     }
  809. #endif
  810.  
  811.     libpath[1] = (char *)NULL;        /* Assume no libpath present */
  812.  
  813.     if (foundcfg) {
  814.     /* assume cfg is open file pointer to config.sys */
  815.     while (fgets((s = line), 4096, cfg)) {
  816.         while (*s == ' ') s++;              /* remove leading blanks */
  817.         if (strnicmp(s, "LIBPATH", 7) == 0) {
  818.         /* get rid of trailing newline, if any */
  819.         if ((p=strrchr(s, '\n')) != NULL)
  820.             *p = '\0';
  821.         Trace((stderr, "found LIBPATH line:\n%s\n", line));
  822.         p = strchr(s+7, '=');
  823.         if (p != NULL) {
  824.             while (*++p == ' ') ;
  825.             libpath[1] = p;
  826.         }
  827.         break;
  828.         }
  829.     }
  830.     fclose (cfg);
  831.     }
  832.  
  833.     /*
  834.      * Here with libpath[1] either null (because no LIBPATH was found in a config.sys
  835.      * file) or pointing into line[] to the start of the path data.  Now to grab the
  836.      * begin and end libpaths, allocate a buffer large enough for all of them, and
  837.      * glue the whole thing together in the appropriate order.
  838.      */
  839. #ifdef __32BIT__
  840.  
  841.     libpath[0] = malloc (512);
  842.     libpath[2] = malloc (512);
  843.  
  844.     if (!libpath[0] || !libpath[2]) {
  845.     fprintf (stderr, "No memory!\n");
  846.     exit (8);
  847.     }
  848.  
  849.     *libpath[0] = '\0';
  850.     *libpath[2] = '\0';
  851.  
  852.     rc = DosQueryExtLIBPATH (libpath[0], BEGIN_LIBPATH);
  853.  
  854.     if (rc != NO_ERROR)
  855.     fprintf (stderr, "Error %ld (0x%0lx) obtaining BEGINLIBPATH!\n", rc, rc);
  856.  
  857.     rc = DosQueryExtLIBPATH (libpath[2], END_LIBPATH);
  858.  
  859.     if (rc != NO_ERROR)
  860.     fprintf (stderr, "Error %ld (0x%0lx) obtaining ENDLIBPATH!\n", rc, rc);
  861. #else
  862.     /* Getting libpaths by getenv() is not best, but try ... */
  863.     libpath[0] = getenv ("BEGINLIBPATH");
  864.     libpath[2] = getenv ("ENDLIBPATH");
  865. #endif
  866.  
  867.     len = 0;
  868.     for (i = 0; i < 3; ++i ) {
  869.     if (libpath[i] && *libpath[i])
  870.         len += strlen (libpath[i]) + 1;    /* extra space for a semicolon */
  871.     }
  872.     if (len == 0) {        /* no library path */
  873. #ifdef __32BIT__
  874.     free (libpath[0]);
  875.     free (libpath[2]);
  876. #endif
  877.     return (-1);
  878.     }
  879.     p = malloc (len + 1);
  880.     if (! p) {
  881.     fprintf (stderr, "No memory!\n");
  882.     exit (8);
  883.     }
  884.  
  885.     *path = p;
  886.  
  887.     for (i = 0; i < 3; i++) {            /* merge library paths */
  888.     if (libpath[i] && *libpath[i]) {
  889.         strcpy (p, libpath[i]);
  890.         p += strlen (p);
  891.         if (p[-1] != ';') *p++ = ';';
  892.     }
  893.     }
  894.     *p = '\0';          /* Terminate the path */
  895. #ifdef __32BIT__
  896.     free (libpath[0]);
  897.     free (libpath[2]);
  898. #endif
  899.     return (0);
  900. }
  901.  
  902. /*--------------------------------------------------------------------------*/
  903. int printmsg (char *msg, int pos)
  904. /*--------------------------------------------------------------------------*/
  905. /* Smartly print msg at the column pos with comma-and-space delimited
  906.  * if the msg can fit in the 80-column screen width, and return the next
  907.  * column postion to be printed.  If msg would bump to screen border, msg is
  908.  * printed at the next line after a space.
  909.  */
  910. {
  911.     int mode;
  912.  
  913.     mode = pos;
  914.     if (mode > 0) {
  915.     printf (",");
  916.     pos++;
  917.     } else if (mode < 0) {
  918.     pos = -pos;
  919.     }
  920.     pos += strlen(msg);
  921.     if (pos >= 78) {        /* if not fit in screen */
  922.     if (mode)        /* if previous pos was not zero */
  923.         printf ("\n");
  924.     pos = strlen(msg);
  925.     }
  926.     if (mode > 0) {
  927.     printf (" ");
  928.     pos++;
  929.     }
  930.     printf ("%s", msg);
  931.     return (pos);
  932. }
  933.  
  934. /*----------------------------------------------------------------------*/
  935. void    pretty_path (char *path, char dir_sep)
  936. /*----------------------------------------------------------------------*/
  937. /* convert path to either unix style or OS/2 (DOS) style */
  938. {
  939.     char bad_sep;
  940.  
  941.     switch (dir_sep) {
  942.     case '\\':
  943.     bad_sep = '/';
  944.     break;
  945.     case '/':
  946.     bad_sep = '\\';
  947.     break;
  948.     default:
  949.     return;
  950.     break;
  951.     }
  952.     for ( ; *path; path++) {
  953.     if (*path == bad_sep) *path = dir_sep;
  954.     }
  955. }
  956.  
  957.